package top.xia17.plugins.fast.common.utils;

import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 将对象数组转换成entity
 * 一个用途是在jpa自定义查询的时候使用，jpa自定义查询如果结果不是实体那么会报错，所以用最原始的方法查出Object[] , 然后通过反射获取构造函数转成entity
 * 注意： 实体的构造函数需要使用 {@link ObjArray2EntitySign} 注解来进行标记
 * @author xia17
 * @date 2020/8/4 9:57
 */
@Slf4j
public class ObjArray2EntityUtil {

    /**
     * 说明: 将JPA查询的List<Object[]>结果集封装到自定义对象中
     * @param list 待处理数据
     * @param clazz 待映射对象类型，该对象的构造方法需要结合@Sign注解使用
     * @param sign 待映射对象构造器上的注解值，用于匹配不同的结果集映射
     * @param <T> 待映射对象泛型
     * @return 映射后的结果集
     *
     */
    public static <T> List<T> mapping(List<Object[]> list, Class<T> clazz, int sign) {
        Constructor<T> constructor = getConstructor(clazz, sign);
        return list.stream()
                .map(data->mapping(data,constructor))
                .collect(Collectors.toList());
    }

    /**
     * 说明: 将JPA查询的List<Object[]>结果集封装到自定义对象中
     *      为默认的注解值提供一个便捷的方法，不用传递注解参数0
     */
    public static <T> List<T> mapping(List<Object[]> list, Class<T> clazz) {
        return mapping(list, clazz, 0);
    }

    public static<T> T mapping(@NotNull Object[] data, Class<T> clz){
        return mapping(data,getConstructor(clz,0));
    }

    public static<T> T mapping(@NotNull Object[] data, Class<T> clz, int sign){
        return mapping(data,getConstructor(clz,sign));
    }

    private static<T> T mapping(Object[] data , Constructor<T> constructor){
        try {
            return constructor.newInstance(data);
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
            log.error("使用构造器实例化时出现异常",e);
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取构造器
     * @param clz clz
     * @param sign /
     * @param <T> /
     * @return /
     */
    @SuppressWarnings("unchecked")
    public static<T> Constructor<T> getConstructor(Class<T> clz , int sign){
        Constructor<?>[] constructors = clz.getConstructors();
        for (Constructor<?> constructor : constructors) {
            if (constructor.isAnnotationPresent(ObjArray2EntitySign.class)) {
                if (sign == constructor.getAnnotation(ObjArray2EntitySign.class).value()) {
                   return (Constructor<T>) constructor;
                }
            }
        }
        log.error("未找到指定构造器");
        throw new RuntimeException("未找到指定构造器");
    }



}
